
Alkaline.Processing.VariableDefinition = function(name, sourceType, declarationToken, startVal) {
	this.name = name;
	this.sourceType = sourceType;
	if (!startVal)
		if (sourceType === Alkaline.Processing.VariableDefinition.SOURCE_TYPE.DETECTED)
			startVal = new Alkaline.Processing.ReferencedValue(Alkaline.Processing.ReferencedValue.VALUE_TYPE.UNKNOWN, {});
		else
			startVal = Alkaline.Processing.ValueUndefined.instance;
	this.valueDistribution = this.startVal = new Alkaline.Processing.ValueDistribution([{ condition: Alkaline.Processing.Condition.ALWAYS, value: startVal }]);
	this.referencedFrom = [];
	this.declarationToken = declarationToken || null;
	this.closureReferenceCount = 0;	// how many references to this variable from internal contexts? if 0, this is a strictly local variable
}

Alkaline.Processing.VariableDefinition.SOURCE_TYPE = {
	IMPLIED:		0,	// function name, declared arguments, etc.
	DECLARED_VAR:	1,
	DECLARED_LET:	2,
	DECLARED_CONST:	3,
	NATIVE:		4,	// e.g. "arguments" object
	BUILT_IN:		4,
	DETECTED:		5,
	FROM_HEADER:	6
}

// these are set when the variable is added to a LexicalContext:
Alkaline.Processing.VariableDefinition.prototype.lexicalContext = null;
Alkaline.Processing.VariableDefinition.prototype.immutable = null;

Alkaline.Processing.VariableDefinition.prototype.setValue = function(condition, newValue) {
	this.valueDistribution.setValue(condition, newValue);
}

Alkaline.Processing.VariableDefinition.prototype.getValue = function(condition) {
	return this.valueDistribution.getValue(condition);
}

Alkaline.Processing.VariableDefinition.prototype.getPropertyDescriptor = function(condition, name) {
	return this.valueDistribution.getPropertyDescriptor(condition, name);
}

Alkaline.Processing.VariableDefinition.prototype.addReference = function(ast) {
	this.referencedFrom.push(ast);
	if (!Alkaline.TreeBuilder.isSubtree(ast, this.lexicalContext.BLOCK, true))
		this.closureReferenceCount++;
}

Alkaline.Processing.VariableDefinition.prototype.removeReference = function(ast) {
	for (var i = 0; i < this.referencedFrom.length; i++)
		if (this.referencedFrom[i] === ast) {
			this.referencedFrom.splice(i, 1);
			if (!Alkaline.TreeBuilder.isSubtree(ast, this.lexicalContext.BLOCK, true))
				this.closureReferenceCount--;
			break;
		}
}

Alkaline.Processing.VariableDefinition.prototype.toString = function() {
	return this.name + ' (' + this.sourceType + ', ' + this.valueDistribution.values.length + ' value' + (this.valueDistribution.values.length != 1 ? 's' : '') +')';
}

Alkaline.Processing.VariableDefinition.prototype.renameAllReferences = function(newVariable) {
	for (var i = 0; i < this.referencedFrom.length; i++)
		this.referencedFrom[i].token.text = newVariable.name;
	newVariable.referencedFrom = newVariable.referencedFrom.concat(this.referencedFrom);
	this.referencedFrom = [];
}

Alkaline.Processing.VariableDefinition.prototype.remove = function() {
	if (this.declarationToken) {
		this.lexicalContext.builder.removeTree(this.declarationToken);
		this.declarationToken = null;
	}
}

Alkaline.Processing.VariableDefinition.prototype.renameTo = function(newName) {
	if (this.lexicalContext)
		this.lexicalContext._renameVariable(this.name, newName);
	this.name = newName;
	if (this.declarationToken)
		this.declarationToken.token.text = newName;
	for (var i = 0; i < this.referencedFrom.length; i++)
		this.referencedFrom[i].token.text = newName;
}

Alkaline.Processing.VariableDefinition.prototype.declare = function() {
	if (this.sourceType === Alkaline.Processing.VariableDefinition.SOURCE_TYPE.IMPLIED && !this.declarationToken) {
		this.sourceType = Alkaline.Processing.VariableDefinition.SOURCE_TYPE.DECLARED_VAR;
		this.declarationToken = this.lexicalContext.builder.Id(this.name);
		this.lexicalContext._mustHaveVarDecl();
		this.lexicalContext.BLOCK.children[0].addChild(this.declarationToken);
	}
}
